Let’s import at all of the packages and the data (RS and Geographic):
library(tidyverse)
library(gridExtra)
library(maps)
library(ggthemes)
library(ggforce)
DeltasClean <- read_csv("../data/out/deltas_clean_v2.csv")
Parsed with column specification:
cols(
Delta = [31mcol_character()[39m,
location = [31mcol_character()[39m,
surface = [31mcol_character()[39m,
year = [32mcol_double()[39m,
month = [32mcol_double()[39m,
ndvi = [32mcol_double()[39m,
red = [32mcol_double()[39m,
evi = [32mcol_double()[39m,
savi = [32mcol_double()[39m,
gr = [32mcol_double()[39m,
ndssi = [32mcol_double()[39m
)
DeltaLocations <- read_csv("../data/DeltaLocations.csv")
Parsed with column specification:
cols(
Deltas = [31mcol_character()[39m,
Lat = [32mcol_double()[39m,
Lon = [32mcol_double()[39m
)
As a reminder, for each of the 47 deltas there are measurements of Land & Water areas at Upstream, Downstream and ‘Middle’ locations on the delta. We first lump all the observations together, and look to see which Deltas have many observations:
#counts per delta
count(DeltasClean, Delta)
NA
Now, by each month.. where the colorbar represents the number of observations (n) for each month for a given delta:
ggplot(count(DeltasClean, Delta, month),
aes(y = Delta, x = month, fill=n)) +
geom_tile() +
scale_x_discrete(limits = c(1:12), breaks = c(1:12)) +
expand_limits(x = c(1,12)) +
scale_fill_gradient( trans = 'log' )

In the above heat map, dark colors (and no color) represent data paucity (and data gaps). Deltas with light colors (e.g., the Parana, Nile, Ebro, Colorado, Brahmani) have lots of data, spread out through the months of the year.
I’ll remove/subset the deltas with sparse coverage (specifically, months with no coverage)….
# need 10 data points per month for NDSSI and NDVI
EnoughObsPerMonth <- DeltasClean %>% ungroup() %>%
count(Delta, month, surface) %>%
group_by(surface) %>%
filter( n >= 5)
#find deltas missing a given month of observations
DeltaMonthCounts <- EnoughObsPerMonth %>%
ungroup() %>%
count(Delta)
# need 12 months of water and land obs, so 24 mo total
EnoughMonths <- DeltaMonthCounts %>%
filter( n == 24)
CompleteObsDeltas <- pull(EnoughMonths, Delta)
#remove them
DeltasCleaner <- DeltasClean %>%
filter(Delta %in% CompleteObsDeltas)
#add the real dates in month date format
DeltasCleaner$date <- as.Date(paste(DeltasCleaner$year, DeltasCleaner$month, "01", sep="-"), "%Y-%m-%d")
#remove intermediate data
rm(CompleteObsDeltas, EnoughMonths, EnoughObsPerMonth, DeltaMonthCounts, DeltasClean)
Ok, now let’s organize this raw data into 1 dataframe, with delta, max and min NDVI (and month), max and min NDSSI(and month), NDVI and NDSSI range.
#take the mean NDVI and NDSSI for each month, for each delta
DeltaMeans <- DeltasCleaner %>%
group_by(Delta, month, surface) %>%
summarize(MeanNDVI = mean(ndvi, na.rm = TRUE), MeanNDSSI = mean(ndssi, na.rm = TRUE))
#make a 9 column data frame with:
#delta,
#max and min NDVI month,
#NDSSI max and min month,
#max and min values for both NDVI and NDSSI
#####
DeltaMaxNDVI <-
DeltaMeans %>%
filter(surface == 'Land') %>%
select (-c(MeanNDSSI)) %>%
group_by(Delta) %>%
slice(which.max(MeanNDVI)) %>%
rename(MaxMeanNDVImonth = month, MaxMeanNDVI = MeanNDVI)
DeltaMaxNDSSI <-
DeltaMeans %>%
filter(surface == 'Water') %>%
select (-c(MeanNDVI)) %>%
group_by(Delta) %>%
slice(which.max(MeanNDSSI)) %>%
rename(MaxMeanNDSSImonth = month, MaxMeanNDSSI = MeanNDSSI)
DeltaMinNDVI <-
DeltaMeans %>%
filter(surface == 'Land') %>%
select (-c(MeanNDSSI)) %>%
group_by(Delta) %>%
slice(which.min(MeanNDVI)) %>%
rename(MinMeanNDVImonth = month, MinMeanNDVI = MeanNDVI)
DeltaMinNDSSI <-
DeltaMeans %>%
filter(surface == 'Water') %>%
select (-c(MeanNDVI)) %>%
group_by(Delta) %>%
slice(which.min(MeanNDSSI)) %>%
rename(MinMeanNDSSImonth = month, MinMeanNDSSI = MeanNDSSI)
#join into 1 dataframe
DeltaMaxMin <- left_join(DeltaMaxNDVI, DeltaMaxNDSSI, by = 'Delta') %>%
left_join(.,DeltaMinNDVI, by = 'Delta') %>%
left_join(.,DeltaMinNDSSI, by = 'Delta')
#remove intermediate data
rm(DeltaMaxNDVI, DeltaMaxNDSSI, DeltaMinNDSSI,DeltaMinNDVI)
DeltaMaxMin <- DeltaMaxMin %>%
select(-surface.x, -surface.x.x, -surface.y, -surface.y.y)
And now we calculate phase shifts between NDVI and NDSSI for each delta
#compare offset
DeltaMaxMin <- mutate(DeltaMaxMin,
MinOffset = if_else(MinMeanNDVImonth > MinMeanNDSSImonth,
MinMeanNDVImonth - MinMeanNDSSImonth,
MinMeanNDSSImonth - MinMeanNDVImonth),
MaxOffset = if_else(MaxMeanNDVImonth > MaxMeanNDSSImonth,
MaxMeanNDVImonth - MaxMeanNDSSImonth,
MaxMeanNDSSImonth - MaxMeanNDVImonth),
OffsetDiff = abs(MaxOffset - MinOffset),
rangeNDVI = (MaxMeanNDVI - MinMeanNDVI),
rangeNDSSI = (MaxMeanNDSSI - MinMeanNDSSI)
)
DeltaMaxMin <- DeltaMaxMin %>%
mutate(MinOffset = ifelse(MinOffset > 6, (-1*MinOffset)+12 ,MinOffset))
DeltaMaxMin <- DeltaMaxMin %>%
mutate(MaxOffset = ifelse(MaxOffset > 6, (-1*MaxOffset)+12 ,MaxOffset))
DeltaMaxMin
ggplot(DeltaMaxMin, aes(y = Delta, x = MaxOffset)) + geom_point() +
scale_x_discrete(limits = c(1:6), breaks = c(1:6)) +
expand_limits(x = c(0,6)) +
ggtitle("MaxOffset")

ggplot(DeltaMaxMin, aes(y = Delta, x = MinOffset)) + geom_point() +
scale_x_discrete(limits = c(1:6), breaks = c(1:6)) +
expand_limits(x = c(0,6)) +
ggtitle("MinOffset")

ggplot(DeltaMaxMin, aes(y = Delta, x = OffsetDiff)) + geom_point() +
scale_x_discrete(limits = c(1:6), breaks = c(1:6)) +
expand_limits(x = c(0,6)) +
ggtitle("Offset Difference")

Now let’s examine the histograms of all 31 deltas… The months with the greatest mean NDVI, months with gretaest mean NDSSI, the monthly offset, and the skew of the NDSSI and NDVI timeseries.
ggplot(DeltaMaxMin, aes(x = MaxMeanNDVImonth)) +
geom_bar(fill=3) +
scale_x_discrete(limits = c(1:12), breaks = c(1:12)) +
labs(x = "Month") +
ggtitle("Month of maximum mean NDVI")

ggplot(DeltaMaxMin, aes(x = MaxMeanNDSSImonth)) +
geom_bar() +
scale_x_discrete(limits = c(1:12), breaks = c(1:12)) +
labs(x = "Month") +
ggtitle("Month of maximum mean NDSSI")

ggplot(DeltaMaxMin, aes(x = MaxOffset)) +
geom_bar() +
scale_x_discrete(limits = c(0:6), breaks = c(0:6)) +
labs(x = "Months") +
ggtitle("Months Offset between NDVI and NDSSI")

ggplot(data= DeltaMaxMin) +
geom_bar(aes(x=MaxMeanNDVImonth),fill=3, alpha = 0.6) +
geom_bar(aes(x=MaxMeanNDSSImonth),fill=1, alpha = 0.6) +
labs(x = "NDVI") +
ggtitle("Month of maximum mean NDSSI")

Ok, so the idea is that peak NDSSI is more effective if it occurs at moderate NDVI, so let’s look at the NDVI value for the months with peak NDSSI.
#extract NDVI value for each delta a the month of max NDSSI value
MaxNDSSI <- DeltaMaxMin %>%
select(Delta,MaxMeanNDSSImonth) %>%
left_join(filter(DeltaMeans,surface == 'Land'),
by = c('Delta', 'MaxMeanNDSSImonth' ='month')) %>%
select (-c(surface, MeanNDSSI)) %>%
mutate(maxmonthNDVI = MeanNDVI) %>%
select (-c(MeanNDVI,MaxMeanNDSSImonth))
#extract NDVI value for each delta at one month earlier than max NDSSI value
MaxNDSSIEarly <- DeltaMaxMin %>%
select(Delta,MaxMeanNDSSImonth) %>%
mutate(EarlyNDSSI = if_else(MaxMeanNDSSImonth == 1, 12, MaxMeanNDSSImonth-1)) %>%
select (-c(MaxMeanNDSSImonth)) %>%
left_join(filter(DeltaMeans,surface == 'Land'),
by = c('Delta', 'EarlyNDSSI' ='month')) %>%
select (-c(surface, MeanNDSSI)) %>%
mutate(EarlyNDVI = MeanNDVI) %>%
select (-c(MeanNDVI,EarlyNDSSI))
joinedPeakNDSSI <- left_join(MaxNDSSI, MaxNDSSIEarly, by = c('Delta'))
#remove intermediate data
rm(MaxNDSSI, MaxNDSSIEarly)
ggplot(data= joinedPeakNDSSI) +
geom_density(aes(x=maxmonthNDVI),fill=1, alpha = 0.6) +
geom_density(aes(x=EarlyNDVI),fill=3, alpha = 0.6) +
xlim(0,1) +
labs(x = "NDVI") +
ggtitle("NDVI at month of maximum mean NDSSI and one month before")

Just to explore the data a bit, here are offsets against other measured parameters for each delta. The range, max and mean of NDVI and NDSSI is calculated from the timeseries, so it is really the max, min, and range of the monthly means (i.e., the maximum of the means, the minimum of the means, and the range of the mean). Offset is measured in months.
ggplot(data = select(DeltaMaxMin,rangeNDVI,MaxOffset,
rangeNDSSI,MaxMeanNDVI,MaxMeanNDSSI)) +
geom_autopoint(na.rm = TRUE) +
facet_matrix(rows = vars(rangeNDVI:MaxMeanNDSSI),
switch = "x") + theme(aspect.ratio=1)
Adding missing grouping variables: `Delta`

Join Latitude and longitude data
DeltaDatawLocations <- left_join(select(DeltaMaxMin,rangeNDVI,MaxOffset,
rangeNDSSI,MaxMeanNDVI,MaxMeanNDSSI),
DeltaLocations, by = c("Delta" = "Deltas"))
Adding missing grouping variables: `Delta`
DeltaDatawLocations <- DeltaDatawLocations %>%
mutate(Absolute_Latitude= abs(Lat))
ggplot(data = DeltaDatawLocations) +
geom_autopoint(na.rm = TRUE) +
facet_matrix(cols = vars(rangeNDVI:MaxMeanNDSSI), rows = vars(Absolute_Latitude) ,
switch = "x") + theme(aspect.ratio=1)

So Offset and Lat seem to have a relationship:
#find the linear model
DeltaOffset_lm <- lm( Absolute_Latitude ~ MaxOffset, data = DeltaDatawLocations)
summary(DeltaOffset_lm)
Call:
lm(formula = Absolute_Latitude ~ MaxOffset, data = DeltaDatawLocations)
Residuals:
Min 1Q Median 3Q Max
-21.779 -8.983 0.775 8.377 20.772
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 10.820 4.629 2.337 0.02653 *
MaxOffset 4.079 1.157 3.525 0.00143 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 11.06 on 29 degrees of freedom
Multiple R-squared: 0.2999, Adjusted R-squared: 0.2758
F-statistic: 12.43 on 1 and 29 DF, p-value: 0.001428
ggplot(DeltaDatawLocations, aes(x = Absolute_Latitude, y = MaxOffset)) +
geom_point() +
geom_smooth(mapping = aes(x = Absolute_Latitude, y = MaxOffset, ), method=lm )

Now for some maps of the data maps:
world <- ggplot() +
borders("world", colour = "gray85", fill = "gray80") +
theme_map()
DeltaOffsetMap <- world +
geom_point(aes(x = Lon, y = Lat, color = MaxOffset),
data = DeltaDatawLocations,
size = 5) + scale_color_gradient( high = "red", low = "yellow") +
ggtitle("Offset Between NDVI peak on Land and NDSSI peak in water")
world <- ggplot() +
borders("world", colour = "gray85", fill = "gray80") +
theme_map()
DeltaNDVIrangeMap <- world +
geom_point(aes(x = Lon, y = Lat, color = rangeNDVI),
data = DeltaDatawLocations,
size = 5) + scale_color_gradient( high = "red", low = "yellow") +
ggtitle("NDVI range")
DeltaNDSSIrangeMap <- world +
geom_point(aes(x = Lon, y = Lat, color = rangeNDSSI),
data = DeltaDatawLocations,
size = 5) + scale_color_gradient( high = "red", low = "yellow") + ggtitle("NDSSI range")
DeltaOffsetMap

DeltaNDVIrangeMap

DeltaNDSSIrangeMap

#ggsave("DeltaOffsetMap.pdf", width = 6, height = 4)
#ggsave("DeltaNDVIrangeMap.pdf", width = 6, height = 4)
#ggsave("DeltaNDSSIrangeMap.pdf", width = 6, height = 4)
Let’s look at some of the timeseries To quantify the water, we use NDSSI. to quantify land, we use NDVI.
First here is the function to make the plots:
DeltaPlotter <- function(DeltaName) {
#Counts each month
numVeg <- DeltasCleaner %>%
select(Delta, surface, month, ndvi) %>%
filter(Delta == DeltaName & surface == "Land" & !is.na(ndvi)) %>%
group_by(month) %>%
summarize(n = n())
numSed <- DeltasCleaner %>%
select(Delta, surface, month, ndssi) %>%
filter(Delta == DeltaName &
surface == "Water" & !is.na(ndssi)) %>%
group_by(month) %>%
summarize(n = n())
#Highlight the Maximum and Minimum Month for each delta, NDVI and NDSSI
#LAND
Veg <-
ggplot(data = filter(DeltasCleaner, Delta == DeltaName &
surface == "Land")) +
geom_boxplot(aes(x = month, y = ndvi, group = month)) +
scale_x_discrete(limits = c(1:12), breaks = c(1:12)) +
expand_limits(x = c(1, 12)) +
ggtitle(DeltaName) +
#geom_text(data = numVeg, aes(y = 1.05, x = month, label = n)) +
geom_boxplot(
data = filter(
DeltasCleaner,
Delta == DeltaName &
surface == "Land" & month == DeltaMaxMin$MaxMeanNDVImonth[DeltaMaxMin$Delta == DeltaName]
),
aes(x = month, y = ndvi, group = month),
fill = "green"
) +
geom_boxplot(
data = filter(
DeltasCleaner,
Delta == DeltaName &
surface == "Land" & month == DeltaMaxMin$MinMeanNDVImonth[DeltaMaxMin$Delta ==DeltaName]
),
aes(x = month, y = ndvi, group = month),
fill = "blue"
)
Sed <-
ggplot(data = filter(DeltasCleaner, Delta == DeltaName &
surface == "Water")) +
geom_boxplot(aes(x = month, y = ndssi, group = month)) +
scale_x_discrete(limits = c(1:12), breaks = c(1:12)) +
expand_limits(x = c(1, 12)) +
#geom_text(data = numSed, aes(y = 1.05, x = month, label = n)) +
geom_boxplot(
data = filter(
DeltasCleaner,
Delta == DeltaName &
surface == "Water" & month == DeltaMaxMin$MaxMeanNDSSImonth[DeltaMaxMin$Delta == DeltaName]
),
aes(x = month, y = ndssi, group = month),
fill = "green"
) +
geom_boxplot(
data = filter(
DeltasCleaner,
Delta == DeltaName &
surface == "Water" & month == DeltaMaxMin$MinMeanNDSSImonth[DeltaMaxMin$Delta == DeltaName]
),
aes(x = month, y = ndssi, group = month),
fill = "blue"
)
return(grid.arrange(Veg, Sed, nrow = 2))
}
Here is are some examples:
- The peaks in both timeseries shift around depending on the delta:
- look at the correlation in the Orinoco nad Senegal
- The anticorrelation in the Parana and Ebro,
- The slight phase shift in the Magdalena.
DeltaPlotter("Parana")

DeltaPlotter("Magdalena")

DeltaPlotter("Ebro")

DeltaPlotter("Nile")

DeltaPlotter("Senegal")

DeltaPlotter("Orinoco")

DeltaPlotter("Godavari")

DeltaPlotter("Krishna")

And Finally, we can look at GRDC data:
#import the data (monthly means for 21 stations)
DeltasGRDC <- read_csv("../data/GRDCstations.csv")
Parsed with column specification:
cols(
Deltas = [31mcol_character()[39m,
GRDC_Station = [32mcol_double()[39m,
Time_Series_Length = [31mcol_character()[39m,
January = [32mcol_double()[39m,
February = [32mcol_double()[39m,
March = [32mcol_double()[39m,
April = [32mcol_double()[39m,
May = [32mcol_double()[39m,
June = [32mcol_double()[39m,
July = [32mcol_double()[39m,
August = [32mcol_double()[39m,
September = [32mcol_double()[39m,
October = [32mcol_double()[39m,
November = [32mcol_double()[39m,
December = [32mcol_double()[39m
)
#calculate the mean of the monthly means
DeltasGRDC <- DeltasGRDC %>%
rowwise() %>%
mutate(MMD=mean(c(January,February,March,April,
May,June,July,August,
September,October,November,December))) %>%
rowwise() %>%
mutate(Range_Discharge = max(c(January,February,March,April,
May,June,July,August,
September,October,November,December)) -
min(c(January,February,March,April,
May,June,July,August,
September,October,November,December))
) %>%
right_join(DeltaDatawLocations, by = c("Deltas" = "Delta"))
#rename the months by numbers and tidy the GRDC data
DeltasDischarge <- DeltasGRDC %>%
rename(Delta = Deltas,"1" = January, "2"= February, "3"= March, "4"= April,
"5"=May, "6"=June, "7"=July, "8"= August, "9" = September, "10"=October,
"11"=November, "12"=December) %>%
select(Delta, "1" , "2" , "3", "4","5", "6", "7", "8", "9", "10", "11", "12") %>%
pivot_longer(-Delta, names_to = "month", values_to = "discharge")
DeltasDischarge$month = as.numeric(DeltasDischarge$month)
DisNDSSI <- DeltaMeans %>%
filter(surface == 'Water') %>%
select (-c(MeanNDVI)) %>%
left_join(DeltasDischarge, by = c('Delta', 'month')) %>%
drop_na(discharge)
#plot monthly means against NDSSI for a given delta
ggplot(DisNDSSI, aes(y=discharge, x=MeanNDSSI)) + geom_point(shape=1) + facet_wrap( ~ Delta, ncol=4, , scales="free_y")

NA
NA
LS0tCnRpdGxlOiAiRGVsdGFzIE5vdGVib29rIDIiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgpMZXQncyBpbXBvcnQgYXQgYWxsIG9mIHRoZSBwYWNrYWdlcyBhbmQgdGhlIGRhdGEgKFJTIGFuZCBHZW9ncmFwaGljKToKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkobWFwcykKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShnZ2ZvcmNlKQoKRGVsdGFzQ2xlYW4gPC0gcmVhZF9jc3YoIi4uL2RhdGEvb3V0L2RlbHRhc19jbGVhbl92Mi5jc3YiKSAKRGVsdGFMb2NhdGlvbnMgPC0gcmVhZF9jc3YoIi4uL2RhdGEvRGVsdGFMb2NhdGlvbnMuY3N2IikKYGBgCgpBcyBhIHJlbWluZGVyLCBmb3IgZWFjaCBvZiB0aGUgNDcgZGVsdGFzIHRoZXJlIGFyZSBtZWFzdXJlbWVudHMgb2YgTGFuZCAmIFdhdGVyIGFyZWFzIGF0IFVwc3RyZWFtLCBEb3duc3RyZWFtIGFuZCAnTWlkZGxlJyBsb2NhdGlvbnMgb24gdGhlIGRlbHRhLiBXZSBmaXJzdCBsdW1wIGFsbCB0aGUgb2JzZXJ2YXRpb25zIHRvZ2V0aGVyLCBhbmQgbG9vayB0byBzZWUgd2hpY2ggRGVsdGFzIGhhdmUgbWFueSBvYnNlcnZhdGlvbnM6CgpgYGB7cn0KI2NvdW50cyBwZXIgZGVsdGEKY291bnQoRGVsdGFzQ2xlYW4sIERlbHRhKQoKYGBgCgpOb3csIGJ5IGVhY2ggbW9udGguLiB3aGVyZSB0aGUgY29sb3JiYXIgcmVwcmVzZW50cyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyAobikgZm9yIGVhY2ggbW9udGggZm9yIGEgZ2l2ZW4gZGVsdGE6CmBgYHtyfQpnZ3Bsb3QoY291bnQoRGVsdGFzQ2xlYW4sIERlbHRhLCBtb250aCksIAogICAgICAgYWVzKHkgPSBEZWx0YSwgeCA9IG1vbnRoLCBmaWxsPW4pKSArIAogIGdlb21fdGlsZSgpICsgCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKDE6MTIpLCBicmVha3MgPSBjKDE6MTIpKSArCiAgZXhwYW5kX2xpbWl0cyh4ID0gYygxLDEyKSkgKyAKICBzY2FsZV9maWxsX2dyYWRpZW50KCB0cmFucyA9ICdsb2cnICkKCmBgYAoKSW4gdGhlIGFib3ZlIGhlYXQgbWFwLCBkYXJrIGNvbG9ycyAoYW5kIG5vIGNvbG9yKSByZXByZXNlbnQgZGF0YSBwYXVjaXR5IChhbmQgZGF0YSBnYXBzKS4gRGVsdGFzIHdpdGggbGlnaHQgY29sb3JzIChlLmcuLCB0aGUgUGFyYW5hLCBOaWxlLCBFYnJvLCBDb2xvcmFkbywgQnJhaG1hbmkpIGhhdmUgbG90cyBvZiBkYXRhLCBzcHJlYWQgb3V0IHRocm91Z2ggdGhlIG1vbnRocyBvZiB0aGUgeWVhci4KCgpJJ2xsIHJlbW92ZS9zdWJzZXQgdGhlIGRlbHRhcyB3aXRoIHNwYXJzZSBjb3ZlcmFnZSAoc3BlY2lmaWNhbGx5LCBtb250aHMgd2l0aCBubyBjb3ZlcmFnZSkuLi4uIAoKYGBge3J9CgojIG5lZWQgMTAgZGF0YSBwb2ludHMgcGVyIG1vbnRoIGZvciBORFNTSSBhbmQgTkRWSQpFbm91Z2hPYnNQZXJNb250aCA8LSBEZWx0YXNDbGVhbiAlPiUgdW5ncm91cCgpICU+JQogIGNvdW50KERlbHRhLCBtb250aCwgc3VyZmFjZSkgJT4lIAogIGdyb3VwX2J5KHN1cmZhY2UpICU+JQogIGZpbHRlciggbiA+PSA1KQoKI2ZpbmQgZGVsdGFzIG1pc3NpbmcgYSBnaXZlbiBtb250aCBvZiBvYnNlcnZhdGlvbnMKRGVsdGFNb250aENvdW50cyA8LSBFbm91Z2hPYnNQZXJNb250aCAlPiUKICB1bmdyb3VwKCkgJT4lCiAgY291bnQoRGVsdGEpCgojIG5lZWQgMTIgbW9udGhzIG9mIHdhdGVyIGFuZCBsYW5kIG9icywgc28gMjQgbW8gdG90YWwKRW5vdWdoTW9udGhzIDwtIERlbHRhTW9udGhDb3VudHMgJT4lCiBmaWx0ZXIoIG4gPT0gMjQpCgpDb21wbGV0ZU9ic0RlbHRhcyA8LSBwdWxsKEVub3VnaE1vbnRocywgRGVsdGEpCgojcmVtb3ZlIHRoZW0KRGVsdGFzQ2xlYW5lciA8LSBEZWx0YXNDbGVhbiAlPiUKICBmaWx0ZXIoRGVsdGEgJWluJSBDb21wbGV0ZU9ic0RlbHRhcykKCiNhZGQgdGhlIHJlYWwgZGF0ZXMgaW4gbW9udGggZGF0ZSBmb3JtYXQKRGVsdGFzQ2xlYW5lciRkYXRlIDwtIGFzLkRhdGUocGFzdGUoRGVsdGFzQ2xlYW5lciR5ZWFyLCBEZWx0YXNDbGVhbmVyJG1vbnRoLCAiMDEiLCBzZXA9Ii0iKSwgIiVZLSVtLSVkIikKCiNyZW1vdmUgaW50ZXJtZWRpYXRlIGRhdGEKcm0oQ29tcGxldGVPYnNEZWx0YXMsIEVub3VnaE1vbnRocywgRW5vdWdoT2JzUGVyTW9udGgsIERlbHRhTW9udGhDb3VudHMsIERlbHRhc0NsZWFuKQoKYGBgCgpPaywgbm93IGxldCdzIG9yZ2FuaXplIHRoaXMgcmF3IGRhdGEgaW50byAxIGRhdGFmcmFtZSwgd2l0aCBkZWx0YSwgbWF4IGFuZCBtaW4gTkRWSSAoYW5kIG1vbnRoKSwgbWF4IGFuZCBtaW4gTkRTU0koYW5kIG1vbnRoKSwgTkRWSSBhbmQgTkRTU0kgcmFuZ2UuCgpgYGB7ciAgaW5jbHVkZSA9IFRSVUV9CiN0YWtlIHRoZSBtZWFuIE5EVkkgYW5kIE5EU1NJIGZvciBlYWNoIG1vbnRoLCBmb3IgZWFjaCBkZWx0YQpEZWx0YU1lYW5zIDwtIERlbHRhc0NsZWFuZXIgJT4lCiAgZ3JvdXBfYnkoRGVsdGEsIG1vbnRoLCBzdXJmYWNlKSAlPiUKICBzdW1tYXJpemUoTWVhbk5EVkkgPSBtZWFuKG5kdmksIG5hLnJtID0gVFJVRSksIE1lYW5ORFNTSSA9IG1lYW4obmRzc2ksIG5hLnJtID0gVFJVRSkpCgojIyMjIwoKRGVsdGFNYXhORFZJIDwtIAogIERlbHRhTWVhbnMgJT4lIAogIGZpbHRlcihzdXJmYWNlID09ICdMYW5kJykgICU+JSAKICBzZWxlY3QgKC1jKE1lYW5ORFNTSSkpICU+JSAKICBncm91cF9ieShEZWx0YSkgJT4lIAogIHNsaWNlKHdoaWNoLm1heChNZWFuTkRWSSkpICU+JSAKICByZW5hbWUoTWF4TWVhbk5EVkltb250aCA9IG1vbnRoLCBNYXhNZWFuTkRWSSA9IE1lYW5ORFZJKQoKRGVsdGFNYXhORFNTSSA8LSAKICBEZWx0YU1lYW5zICU+JSAKICBmaWx0ZXIoc3VyZmFjZSA9PSAnV2F0ZXInKSAgJT4lIAogIHNlbGVjdCAoLWMoTWVhbk5EVkkpKSAlPiUgCiAgZ3JvdXBfYnkoRGVsdGEpICU+JSAKICBzbGljZSh3aGljaC5tYXgoTWVhbk5EU1NJKSkgJT4lIAogIHJlbmFtZShNYXhNZWFuTkRTU0ltb250aCA9IG1vbnRoLCBNYXhNZWFuTkRTU0kgPSBNZWFuTkRTU0kpCgpEZWx0YU1pbk5EVkkgPC0gCiAgRGVsdGFNZWFucyAlPiUgCiAgZmlsdGVyKHN1cmZhY2UgPT0gJ0xhbmQnKSAgJT4lIAogIHNlbGVjdCAoLWMoTWVhbk5EU1NJKSkgJT4lIAogIGdyb3VwX2J5KERlbHRhKSAlPiUgCiAgc2xpY2Uod2hpY2gubWluKE1lYW5ORFZJKSkgJT4lIAogIHJlbmFtZShNaW5NZWFuTkRWSW1vbnRoID0gbW9udGgsIE1pbk1lYW5ORFZJID0gTWVhbk5EVkkpCgpEZWx0YU1pbk5EU1NJIDwtIAogIERlbHRhTWVhbnMgJT4lIAogIGZpbHRlcihzdXJmYWNlID09ICdXYXRlcicpICAlPiUgCiAgc2VsZWN0ICgtYyhNZWFuTkRWSSkpICU+JSAKICBncm91cF9ieShEZWx0YSkgJT4lIAogIHNsaWNlKHdoaWNoLm1pbihNZWFuTkRTU0kpKSAlPiUgCiAgcmVuYW1lKE1pbk1lYW5ORFNTSW1vbnRoID0gbW9udGgsIE1pbk1lYW5ORFNTSSA9IE1lYW5ORFNTSSkKCgojam9pbiBpbnRvIDEgZGF0YWZyYW1lCkRlbHRhTWF4TWluIDwtIGxlZnRfam9pbihEZWx0YU1heE5EVkksIERlbHRhTWF4TkRTU0ksIGJ5ID0gJ0RlbHRhJykgJT4lIAogIGxlZnRfam9pbiguLERlbHRhTWluTkRWSSwgYnkgPSAnRGVsdGEnKSAlPiUgCiAgbGVmdF9qb2luKC4sRGVsdGFNaW5ORFNTSSwgYnkgPSAnRGVsdGEnKSAKCiNyZW1vdmUgaW50ZXJtZWRpYXRlIGRhdGEKcm0oRGVsdGFNYXhORFZJLCBEZWx0YU1heE5EU1NJLCBEZWx0YU1pbk5EU1NJLERlbHRhTWluTkRWSSkKCkRlbHRhTWF4TWluIDwtIERlbHRhTWF4TWluICU+JQogIHNlbGVjdCgtc3VyZmFjZS54LCAtc3VyZmFjZS54LngsIC1zdXJmYWNlLnksIC1zdXJmYWNlLnkueSkKCmBgYApBbmQgbm93IHdlIGNhbGN1bGF0ZSBwaGFzZSBzaGlmdHMgYmV0d2VlbiBORFZJIGFuZCBORFNTSSBmb3IgZWFjaCBkZWx0YQpgYGB7ciB9CiNjb21wYXJlIG9mZnNldApEZWx0YU1heE1pbiA8LSBtdXRhdGUoRGVsdGFNYXhNaW4sIAogICAgICAgICAgICAgICAgICAgICAgTWluT2Zmc2V0ID0gaWZfZWxzZShNaW5NZWFuTkRWSW1vbnRoID4gTWluTWVhbk5EU1NJbW9udGgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNaW5NZWFuTkRWSW1vbnRoIC0gTWluTWVhbk5EU1NJbW9udGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1pbk1lYW5ORFNTSW1vbnRoIC0gTWluTWVhbk5EVkltb250aCksCiAgICAgICAgICAgICAgICAgICAgICBNYXhPZmZzZXQgPSBpZl9lbHNlKE1heE1lYW5ORFZJbW9udGggPiBNYXhNZWFuTkRTU0ltb250aCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1heE1lYW5ORFZJbW9udGggLSBNYXhNZWFuTkRTU0ltb250aCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTWF4TWVhbk5EU1NJbW9udGggLSBNYXhNZWFuTkRWSW1vbnRoKSwKICAgICAgICAgICAgICAgICAgICAgIE9mZnNldERpZmYgPSBhYnMoTWF4T2Zmc2V0IC0gTWluT2Zmc2V0KSwKICAgICAgICAgICAgICAgICAgICAgIHJhbmdlTkRWSSA9IChNYXhNZWFuTkRWSSAtIE1pbk1lYW5ORFZJKSwgCiAgICAgICAgICAgICAgICAgICAgICByYW5nZU5EU1NJID0gKE1heE1lYW5ORFNTSSAtIE1pbk1lYW5ORFNTSSkKICAgICAgICAgICAgICAgICAgICAgICkKCkRlbHRhTWF4TWluIDwtIERlbHRhTWF4TWluICU+JSAKICBtdXRhdGUoTWluT2Zmc2V0ID0gaWZlbHNlKE1pbk9mZnNldCA+IDYsICgtMSpNaW5PZmZzZXQpKzEyICxNaW5PZmZzZXQpKQoKRGVsdGFNYXhNaW4gPC0gRGVsdGFNYXhNaW4gJT4lIAogIG11dGF0ZShNYXhPZmZzZXQgPSBpZmVsc2UoTWF4T2Zmc2V0ID4gNiwgKC0xKk1heE9mZnNldCkrMTIgLE1heE9mZnNldCkpICAgICAgICAKICAKCgpEZWx0YU1heE1pbgoKZ2dwbG90KERlbHRhTWF4TWluLCBhZXMoeSA9IERlbHRhLCB4ID0gTWF4T2Zmc2V0KSkgKyBnZW9tX3BvaW50KCkgKyAKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IGMoMTo2KSwgYnJlYWtzID0gYygxOjYpKSArCiAgZXhwYW5kX2xpbWl0cyh4ID0gYygwLDYpKSAgKyAKICBnZ3RpdGxlKCJNYXhPZmZzZXQiKQoKIyBnZ3Bsb3QoRGVsdGFNYXhNaW4sIGFlcyh5ID0gRGVsdGEsIHggPSBNaW5PZmZzZXQpKSArIGdlb21fcG9pbnQoKSArIAojICAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKDE6NiksIGJyZWFrcyA9IGMoMTo2KSkgKwojICAgZXhwYW5kX2xpbWl0cyh4ID0gYygwLDYpKSAgKyAKIyAgIGdndGl0bGUoIk1pbk9mZnNldCIpCiMgCiMgZ2dwbG90KERlbHRhTWF4TWluLCBhZXMoeSA9IERlbHRhLCB4ID0gT2Zmc2V0RGlmZikpICsgZ2VvbV9wb2ludCgpICsgCiMgICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IGMoMTo2KSwgYnJlYWtzID0gYygxOjYpKSArCiMgICBleHBhbmRfbGltaXRzKHggPSBjKDAsNikpICArIAojICAgZ2d0aXRsZSgiT2Zmc2V0IERpZmZlcmVuY2UiKQoKYGBgCgpOb3cgbGV0J3MgZXhhbWluZSB0aGUgaGlzdG9ncmFtcyBvZiBhbGwgMzEgZGVsdGFzLi4uIFRoZSBtb250aHMgd2l0aCB0aGUgZ3JlYXRlc3QgbWVhbiBORFZJLCBtb250aHMgd2l0aCBncmV0YWVzdCBtZWFuIE5EU1NJLCB0aGUgbW9udGhseSBvZmZzZXQsIGFuZCB0aGUgc2tldyBvZiB0aGUgTkRTU0kgYW5kIE5EVkkgdGltZXNlcmllcy4KCmBgYHtyfQpnZ3Bsb3QoRGVsdGFNYXhNaW4sIGFlcyh4ID0gTWF4TWVhbk5EVkltb250aCkpICsgCiAgZ2VvbV9iYXIoZmlsbD0zKSArIAogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygxOjEyKSwgYnJlYWtzID0gYygxOjEyKSkgKyAKICBsYWJzKHggPSAiTW9udGgiKSArCiAgZ2d0aXRsZSgiTW9udGggb2YgbWF4aW11bSBtZWFuIE5EVkkiKQoKZ2dwbG90KERlbHRhTWF4TWluLCBhZXMoeCA9IE1heE1lYW5ORFNTSW1vbnRoKSkgKyAKICBnZW9tX2JhcigpICsgCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKDE6MTIpLCBicmVha3MgPSBjKDE6MTIpKSArIAogIGxhYnMoeCA9ICJNb250aCIpICsKICBnZ3RpdGxlKCJNb250aCBvZiBtYXhpbXVtIG1lYW4gTkRTU0kiKQoKZ2dwbG90KGRhdGE9IERlbHRhTWF4TWluKSArIAogIGdlb21fYmFyKGFlcyh4PU1heE1lYW5ORFZJbW9udGgpLGZpbGw9MywgYWxwaGEgPSAwLjYpICsgCiAgZ2VvbV9iYXIoYWVzKHg9TWF4TWVhbk5EU1NJbW9udGgpLGZpbGw9MSwgYWxwaGEgPSAwLjYpICsKICBsYWJzKHggPSAiTkRWSSIpICsKICBnZ3RpdGxlKCJNb250aCBvZiBtYXhpbXVtIG1lYW4gTkRTU0kiKQoKZ2dwbG90KERlbHRhTWF4TWluLCBhZXMoeCA9IE1heE9mZnNldCkpICsgCiAgZ2VvbV9iYXIoKSArIAogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygwOjYpLCBicmVha3MgPSBjKDA6NikpICsgCiAgbGFicyh4ID0gIk1vbnRocyIpICsKICBnZ3RpdGxlKCJNb250aHMgT2Zmc2V0IGJldHdlZW4gTkRWSSBhbmQgTkRTU0kiKQoKYGBgCgpPaywgc28gdGhlIGlkZWEgaXMgdGhhdCBwZWFrIE5EU1NJIGlzIG1vcmUgZWZmZWN0aXZlIGlmIGl0IG9jY3VycyBhdCBtb2RlcmF0ZSBORFZJLCBzbyBsZXQncyBsb29rIGF0IHRoZSBORFZJIHZhbHVlIGZvciB0aGUgbW9udGhzIHdpdGggcGVhayBORFNTSS4gCmBgYHtyfQojZXh0cmFjdCBORFZJIHZhbHVlIGZvciBlYWNoIGRlbHRhIGEgdGhlIG1vbnRoIG9mIG1heCBORFNTSSB2YWx1ZQoKTWF4TkRTU0kgPC0gRGVsdGFNYXhNaW4gJT4lCiAgc2VsZWN0KERlbHRhLE1heE1lYW5ORFNTSW1vbnRoKSAlPiUgCiAgbGVmdF9qb2luKGZpbHRlcihEZWx0YU1lYW5zLHN1cmZhY2UgPT0gJ0xhbmQnKSwgCiAgICAgICAgICAgIGJ5ID0gYygnRGVsdGEnLCAnTWF4TWVhbk5EU1NJbW9udGgnID0nbW9udGgnKSkgJT4lCiAgc2VsZWN0ICgtYyhzdXJmYWNlLCBNZWFuTkRTU0kpKSAlPiUKICBtdXRhdGUobWF4bW9udGhORFZJID0gTWVhbk5EVkkpICU+JQogIHNlbGVjdCAoLWMoTWVhbk5EVkksTWF4TWVhbk5EU1NJbW9udGgpKQoKCiNleHRyYWN0IE5EVkkgdmFsdWUgZm9yIGVhY2ggZGVsdGEgYXQgb25lIG1vbnRoIGVhcmxpZXIgdGhhbiBtYXggTkRTU0kgdmFsdWUKCk1heE5EU1NJRWFybHkgPC0gRGVsdGFNYXhNaW4gJT4lCiAgc2VsZWN0KERlbHRhLE1heE1lYW5ORFNTSW1vbnRoKSAlPiUgCiAgbXV0YXRlKEVhcmx5TkRTU0kgPSBpZl9lbHNlKE1heE1lYW5ORFNTSW1vbnRoID09IDEsIDEyLCBNYXhNZWFuTkRTU0ltb250aC0xKSkgJT4lCiAgc2VsZWN0ICgtYyhNYXhNZWFuTkRTU0ltb250aCkpICU+JQogIGxlZnRfam9pbihmaWx0ZXIoRGVsdGFNZWFucyxzdXJmYWNlID09ICdMYW5kJyksIAogICAgICAgICAgICBieSA9IGMoJ0RlbHRhJywgJ0Vhcmx5TkRTU0knID0nbW9udGgnKSkgJT4lCiAgc2VsZWN0ICgtYyhzdXJmYWNlLCBNZWFuTkRTU0kpKSAlPiUKICBtdXRhdGUoRWFybHlORFZJID0gTWVhbk5EVkkpICU+JQogIHNlbGVjdCAoLWMoTWVhbk5EVkksRWFybHlORFNTSSkpCgpqb2luZWRQZWFrTkRTU0kgPC0gbGVmdF9qb2luKE1heE5EU1NJLCBNYXhORFNTSUVhcmx5LCBieSA9IGMoJ0RlbHRhJykpCgojcmVtb3ZlIGludGVybWVkaWF0ZSBkYXRhCnJtKE1heE5EU1NJLCBNYXhORFNTSUVhcmx5KQoKZ2dwbG90KGRhdGE9IGpvaW5lZFBlYWtORFNTSSkgKyAKICBnZW9tX2RlbnNpdHkoYWVzKHg9bWF4bW9udGhORFZJKSxmaWxsPTEsIGFscGhhID0gMC42KSArIAogIGdlb21fZGVuc2l0eShhZXMoeD1FYXJseU5EVkkpLGZpbGw9MywgYWxwaGEgPSAwLjYpICsKICB4bGltKDAsMSkgKwogIGxhYnMoeCA9ICJORFZJIikgKwogIGdndGl0bGUoIk5EVkkgYXQgbW9udGggb2YgbWF4aW11bSBtZWFuIE5EU1NJIGFuZCBvbmUgbW9udGggYmVmb3JlIikKCmBgYAoKSnVzdCB0byBleHBsb3JlIHRoZSBkYXRhIGEgYml0LCBoZXJlIGFyZSBvZmZzZXRzIGFnYWluc3Qgb3RoZXIgbWVhc3VyZWQgcGFyYW1ldGVycyBmb3IgZWFjaCBkZWx0YS4gVGhlIHJhbmdlLCBtYXggYW5kIG1lYW4gb2YgTkRWSSBhbmQgTkRTU0kgaXMgY2FsY3VsYXRlZCBmcm9tIHRoZSB0aW1lc2VyaWVzLCBzbyBpdCBpcyByZWFsbHkgdGhlIG1heCwgbWluLCBhbmQgcmFuZ2Ugb2YgdGhlIG1vbnRobHkgbWVhbnMgKGkuZS4sIHRoZSBtYXhpbXVtIG9mIHRoZSBtZWFucywgdGhlIG1pbmltdW0gb2YgdGhlIG1lYW5zLCBhbmQgdGhlIHJhbmdlIG9mIHRoZSBtZWFuKS4gT2Zmc2V0IGlzIG1lYXN1cmVkIGluIG1vbnRocy4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHNlbGVjdChEZWx0YU1heE1pbixyYW5nZU5EVkksTWF4T2Zmc2V0LAogICAgICAgICAgICAgICAgICAgICAgICAgcmFuZ2VORFNTSSxNYXhNZWFuTkRWSSxNYXhNZWFuTkRTU0kpKSArCiAgZ2VvbV9hdXRvcG9pbnQobmEucm0gPSBUUlVFKSArCiAgZmFjZXRfbWF0cml4KHJvd3MgPSB2YXJzKHJhbmdlTkRWSTpNYXhNZWFuTkRTU0kpLAogICAgICAgICAgICAgICBzd2l0Y2ggPSAieCIpICsgdGhlbWUoYXNwZWN0LnJhdGlvPTEpCgpgYGAKCkpvaW4gTGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBkYXRhCgpgYGB7cn0KRGVsdGFEYXRhd0xvY2F0aW9ucyA8LSBsZWZ0X2pvaW4oc2VsZWN0KERlbHRhTWF4TWluLHJhbmdlTkRWSSxNYXhPZmZzZXQsCiAgICAgICAgICAgICAgICAgICAgICAgICByYW5nZU5EU1NJLE1heE1lYW5ORFZJLE1heE1lYW5ORFNTSSksCiAgICAgICAgICAgICAgICAgICAgICAgICBEZWx0YUxvY2F0aW9ucywgYnkgPSBjKCJEZWx0YSIgPSAiRGVsdGFzIikpCgpEZWx0YURhdGF3TG9jYXRpb25zIDwtIERlbHRhRGF0YXdMb2NhdGlvbnMgJT4lCiAgbXV0YXRlKEFic29sdXRlX0xhdGl0dWRlPSBhYnMoTGF0KSkgCgpnZ3Bsb3QoZGF0YSA9IERlbHRhRGF0YXdMb2NhdGlvbnMpICsKICBnZW9tX2F1dG9wb2ludChuYS5ybSA9IFRSVUUpICsKICBmYWNldF9tYXRyaXgoY29scyA9IHZhcnMocmFuZ2VORFZJOk1heE1lYW5ORFNTSSksIHJvd3MgPSB2YXJzKEFic29sdXRlX0xhdGl0dWRlKSAsCiAgICAgICAgICAgICAgIHN3aXRjaCA9ICJ4IikgKyB0aGVtZShhc3BlY3QucmF0aW89MSkKYGBgCgoKU28gT2Zmc2V0IGFuZCBMYXQgc2VlbSB0byBoYXZlIGEgcmVsYXRpb25zaGlwOgoKCmBgYHtyfQojZmluZCB0aGUgbGluZWFyIG1vZGVsIApEZWx0YU9mZnNldF9sbSA8LSBsbSggQWJzb2x1dGVfTGF0aXR1ZGUgfiBNYXhPZmZzZXQsIGRhdGEgPSBEZWx0YURhdGF3TG9jYXRpb25zKSAKCnN1bW1hcnkoRGVsdGFPZmZzZXRfbG0pCgpnZ3Bsb3QoRGVsdGFEYXRhd0xvY2F0aW9ucywgYWVzKHggPSBBYnNvbHV0ZV9MYXRpdHVkZSwgeSA9IE1heE9mZnNldCkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKHggPSBBYnNvbHV0ZV9MYXRpdHVkZSwgeSA9IE1heE9mZnNldCwgKSwgbWV0aG9kPWxtICkgCgpgYGAKCk5vdyBmb3Igc29tZSBtYXBzIG9mIHRoZSBkYXRhIG1hcHM6CgpgYGB7cn0Kd29ybGQgPC0gZ2dwbG90KCkgKwogIGJvcmRlcnMoIndvcmxkIiwgY29sb3VyID0gImdyYXk4NSIsIGZpbGwgPSAiZ3JheTgwIikgKwogIHRoZW1lX21hcCgpIAoKRGVsdGFPZmZzZXRNYXAgPC0gd29ybGQgKwogIGdlb21fcG9pbnQoYWVzKHggPSBMb24sIHkgPSBMYXQsIGNvbG9yID0gTWF4T2Zmc2V0KSwKICAgICAgICAgICAgIGRhdGEgPSBEZWx0YURhdGF3TG9jYXRpb25zLCAKICAgICAgICAgICAgIHNpemUgPSA1KSArIHNjYWxlX2NvbG9yX2dyYWRpZW50KCBoaWdoID0gInJlZCIsIGxvdyAgPSAieWVsbG93IikgKwogIGdndGl0bGUoIk9mZnNldCBCZXR3ZWVuIE5EVkkgcGVhayBvbiBMYW5kIGFuZCBORFNTSSBwZWFrIGluIHdhdGVyIikKCndvcmxkIDwtIGdncGxvdCgpICsKICBib3JkZXJzKCJ3b3JsZCIsIGNvbG91ciA9ICJncmF5ODUiLCBmaWxsID0gImdyYXk4MCIpICsKICB0aGVtZV9tYXAoKSAKCkRlbHRhTkRWSXJhbmdlTWFwIDwtIHdvcmxkICsKICBnZW9tX3BvaW50KGFlcyh4ID0gTG9uLCB5ID0gTGF0LCBjb2xvciA9IHJhbmdlTkRWSSksCiAgICAgICAgICAgICBkYXRhID0gRGVsdGFEYXRhd0xvY2F0aW9ucywKICAgICAgICAgICAgIHNpemUgPSA1KSArIHNjYWxlX2NvbG9yX2dyYWRpZW50KCBoaWdoID0gInJlZCIsIGxvdyAgPSAieWVsbG93IikgKyAKICBnZ3RpdGxlKCJORFZJIHJhbmdlIikKCgpEZWx0YU5EU1NJcmFuZ2VNYXAgIDwtIHdvcmxkICsKICBnZW9tX3BvaW50KGFlcyh4ID0gTG9uLCB5ID0gTGF0LCBjb2xvciA9IHJhbmdlTkRTU0kpLAogICAgICAgICAgICAgZGF0YSA9IERlbHRhRGF0YXdMb2NhdGlvbnMsIAogICAgICAgICAgICAgc2l6ZSA9IDUpICsgc2NhbGVfY29sb3JfZ3JhZGllbnQoIGhpZ2ggPSAicmVkIiwgbG93ID0gInllbGxvdyIpICsgZ2d0aXRsZSgiTkRTU0kgcmFuZ2UiKSAKCgpEZWx0YU9mZnNldE1hcApEZWx0YU5EVklyYW5nZU1hcCAKRGVsdGFORFNTSXJhbmdlTWFwCgojZ2dzYXZlKCJEZWx0YU9mZnNldE1hcC5wZGYiLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCiNnZ3NhdmUoIkRlbHRhTkRWSXJhbmdlTWFwLnBkZiIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCkKI2dnc2F2ZSgiRGVsdGFORFNTSXJhbmdlTWFwLnBkZiIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCkKCmBgYAoKTGV0J3MgbG9vayBhdCBzb21lIG9mIHRoZSB0aW1lc2VyaWVzIApUbyBxdWFudGlmeSB0aGUgd2F0ZXIsIHdlIHVzZSBORFNTSS4gdG8gcXVhbnRpZnkgbGFuZCwgd2UgdXNlIE5EVkkuCgpGaXJzdCBoZXJlIGlzIHRoZSBmdW5jdGlvbiB0byBtYWtlIHRoZSBwbG90czoKCmBgYHtyfQpEZWx0YVBsb3R0ZXIgPC0gZnVuY3Rpb24oRGVsdGFOYW1lKSB7CiAgI0NvdW50cyBlYWNoIG1vbnRoCiAgbnVtVmVnIDwtIERlbHRhc0NsZWFuZXIgJT4lCiAgICBzZWxlY3QoRGVsdGEsIHN1cmZhY2UsIG1vbnRoLCBuZHZpKSAlPiUKICAgIGZpbHRlcihEZWx0YSA9PSBEZWx0YU5hbWUgJiBzdXJmYWNlID09ICJMYW5kIiAmICFpcy5uYShuZHZpKSkgJT4lCiAgICBncm91cF9ieShtb250aCkgJT4lCiAgICBzdW1tYXJpemUobiA9IG4oKSkKICAKICBudW1TZWQgPC0gRGVsdGFzQ2xlYW5lciAlPiUKICAgIHNlbGVjdChEZWx0YSwgc3VyZmFjZSwgbW9udGgsIG5kc3NpKSAlPiUKICAgIGZpbHRlcihEZWx0YSA9PSBEZWx0YU5hbWUgJgogICAgICAgICAgICAgc3VyZmFjZSA9PSAiV2F0ZXIiICYgIWlzLm5hKG5kc3NpKSkgJT4lCiAgICBncm91cF9ieShtb250aCkgJT4lCiAgICBzdW1tYXJpemUobiA9IG4oKSkKICAKICAjSGlnaGxpZ2h0IHRoZSBNYXhpbXVtIGFuZCBNaW5pbXVtIE1vbnRoIGZvciBlYWNoIGRlbHRhLCBORFZJIGFuZCBORFNTSQogIAogICNMQU5ECiAgVmVnIDwtCiAgICBnZ3Bsb3QoZGF0YSA9IGZpbHRlcihEZWx0YXNDbGVhbmVyLCBEZWx0YSA9PSBEZWx0YU5hbWUgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBzdXJmYWNlID09ICJMYW5kIikpICsKICAgIGdlb21fYm94cGxvdChhZXMoeCA9IG1vbnRoLCB5ID0gbmR2aSwgZ3JvdXAgPSBtb250aCkpICsKICAgIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygxOjEyKSwgYnJlYWtzID0gYygxOjEyKSkgKwogICAgZXhwYW5kX2xpbWl0cyh4ID0gYygxLCAxMikpICsKICAgIGdndGl0bGUoRGVsdGFOYW1lKSArCiAgICAjZ2VvbV90ZXh0KGRhdGEgPSBudW1WZWcsIGFlcyh5ID0gMS4wNSwgeCA9IG1vbnRoLCBsYWJlbCA9IG4pKSArCiAgICBnZW9tX2JveHBsb3QoCiAgICAgIGRhdGEgPSBmaWx0ZXIoCiAgICAgICAgRGVsdGFzQ2xlYW5lciwKICAgICAgICBEZWx0YSA9PSBEZWx0YU5hbWUgJgogICAgICAgICAgc3VyZmFjZSA9PSAiTGFuZCIgJiBtb250aCA9PSBEZWx0YU1heE1pbiRNYXhNZWFuTkRWSW1vbnRoW0RlbHRhTWF4TWluJERlbHRhID09IERlbHRhTmFtZV0gCiAgICAgICksCiAgICAgIGFlcyh4ID0gbW9udGgsIHkgPSBuZHZpLCBncm91cCA9IG1vbnRoKSwKICAgICAgZmlsbCA9ICJncmVlbiIKICAgICkgKwogICAgZ2VvbV9ib3hwbG90KAogICAgICBkYXRhID0gZmlsdGVyKAogICAgICAgIERlbHRhc0NsZWFuZXIsCiAgICAgICAgRGVsdGEgPT0gRGVsdGFOYW1lICYgCiAgICAgICAgICBzdXJmYWNlID09ICJMYW5kIiAmIG1vbnRoID09IERlbHRhTWF4TWluJE1pbk1lYW5ORFZJbW9udGhbRGVsdGFNYXhNaW4kRGVsdGEgPT1EZWx0YU5hbWVdCiAgICAgICksCiAgICAgIGFlcyh4ID0gbW9udGgsIHkgPSBuZHZpLCBncm91cCA9IG1vbnRoKSwKICAgICAgZmlsbCA9ICJibHVlIgogICAgKQogIAogIAogIFNlZCA8LQogICAgZ2dwbG90KGRhdGEgPSBmaWx0ZXIoRGVsdGFzQ2xlYW5lciwgRGVsdGEgPT0gRGVsdGFOYW1lICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VyZmFjZSA9PSAiV2F0ZXIiKSkgKwogICAgZ2VvbV9ib3hwbG90KGFlcyh4ID0gbW9udGgsIHkgPSBuZHNzaSwgZ3JvdXAgPSBtb250aCkpICsKICAgIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygxOjEyKSwgYnJlYWtzID0gYygxOjEyKSkgKwogICAgZXhwYW5kX2xpbWl0cyh4ID0gYygxLCAxMikpICsKICAgICNnZW9tX3RleHQoZGF0YSA9IG51bVNlZCwgYWVzKHkgPSAxLjA1LCB4ID0gbW9udGgsIGxhYmVsID0gbikpICsKICAgIGdlb21fYm94cGxvdCgKICAgICAgZGF0YSA9IGZpbHRlcigKICAgICAgICBEZWx0YXNDbGVhbmVyLAogICAgICAgIERlbHRhID09IERlbHRhTmFtZSAmCiAgICAgICAgICBzdXJmYWNlID09ICJXYXRlciIgJiBtb250aCA9PSBEZWx0YU1heE1pbiRNYXhNZWFuTkRTU0ltb250aFtEZWx0YU1heE1pbiREZWx0YSA9PSBEZWx0YU5hbWVdCiAgICAgICksCiAgICAgIGFlcyh4ID0gbW9udGgsIHkgPSBuZHNzaSwgZ3JvdXAgPSBtb250aCksCiAgICAgIGZpbGwgPSAiZ3JlZW4iCiAgICApICsKICAgIGdlb21fYm94cGxvdCgKICAgICAgZGF0YSA9IGZpbHRlcigKICAgICAgICBEZWx0YXNDbGVhbmVyLAogICAgICAgIERlbHRhID09IERlbHRhTmFtZSAmCiAgICAgICAgICBzdXJmYWNlID09ICJXYXRlciIgJiBtb250aCA9PSBEZWx0YU1heE1pbiRNaW5NZWFuTkRTU0ltb250aFtEZWx0YU1heE1pbiREZWx0YSA9PSBEZWx0YU5hbWVdCiAgICAgICksCiAgICAgIGFlcyh4ID0gbW9udGgsIHkgPSBuZHNzaSwgZ3JvdXAgPSBtb250aCksCiAgICAgIGZpbGwgPSAiYmx1ZSIKICAgICkKICAKICByZXR1cm4oZ3JpZC5hcnJhbmdlKFZlZywgU2VkLCBucm93ID0gMikpCn0KYGBgCgoKSGVyZSBpcyBhcmUgc29tZSBleGFtcGxlczoKCiogVGhlIHBlYWtzIGluIGJvdGggdGltZXNlcmllcyBzaGlmdCBhcm91bmQgZGVwZW5kaW5nIG9uIHRoZSBkZWx0YToKICAgKyBsb29rIGF0IHRoZSBjb3JyZWxhdGlvbiBpbiB0aGUgT3Jpbm9jbyBuYWQgU2VuZWdhbAogICArIFRoZSBhbnRpY29ycmVsYXRpb24gaW4gdGhlIFBhcmFuYSBhbmQgRWJybywKICAgKyBUaGUgc2xpZ2h0IHBoYXNlIHNoaWZ0IGluIHRoZSBNYWdkYWxlbmEuCgpgYGB7cn0KRGVsdGFQbG90dGVyKCJQYXJhbmEiKQpEZWx0YVBsb3R0ZXIoIk1hZ2RhbGVuYSIpCkRlbHRhUGxvdHRlcigiRWJybyIpCkRlbHRhUGxvdHRlcigiTmlsZSIpCkRlbHRhUGxvdHRlcigiU2VuZWdhbCIpCkRlbHRhUGxvdHRlcigiT3Jpbm9jbyIpCkRlbHRhUGxvdHRlcigiR29kYXZhcmkiKQpEZWx0YVBsb3R0ZXIoIktyaXNobmEiKQpgYGAKCkFuZCBGaW5hbGx5LCB3ZSBjYW4gbG9vayBhdCBHUkRDIGRhdGE6CmBgYHtyfQojaW1wb3J0IHRoZSBkYXRhIChtb250aGx5IG1lYW5zIGZvciAyMSBzdGF0aW9ucykKRGVsdGFzR1JEQyAgPC0gcmVhZF9jc3YoIi4uL2RhdGEvR1JEQ3N0YXRpb25zLmNzdiIpCgojY2FsY3VsYXRlIHRoZSBtZWFuIG9mIHRoZSBtb250aGx5IG1lYW5zCkRlbHRhc0dSREMgPC0gRGVsdGFzR1JEQyAlPiUgCiAgICByb3d3aXNlKCkgJT4lIAogICAgbXV0YXRlKE1NRD1tZWFuKGMoSmFudWFyeSxGZWJydWFyeSxNYXJjaCxBcHJpbCwKICAgICAgICAgICAgICAgICAgICBNYXksSnVuZSxKdWx5LEF1Z3VzdCwKICAgICAgICAgICAgICAgICAgICBTZXB0ZW1iZXIsT2N0b2JlcixOb3ZlbWJlcixEZWNlbWJlcikpKSAgJT4lIAogIHJvd3dpc2UoKSAlPiUgCiAgbXV0YXRlKFJhbmdlX0Rpc2NoYXJnZSA9IG1heChjKEphbnVhcnksRmVicnVhcnksTWFyY2gsQXByaWwsCiAgICAgICAgICAgICAgTWF5LEp1bmUsSnVseSxBdWd1c3QsCiAgICAgICAgICAgICAgU2VwdGVtYmVyLE9jdG9iZXIsTm92ZW1iZXIsRGVjZW1iZXIpKSAtIAogICAgICAgICAgIG1pbihjKEphbnVhcnksRmVicnVhcnksTWFyY2gsQXByaWwsCiAgICAgICAgICAgICAgTWF5LEp1bmUsSnVseSxBdWd1c3QsCiAgICAgICAgICAgICAgU2VwdGVtYmVyLE9jdG9iZXIsTm92ZW1iZXIsRGVjZW1iZXIpKQogICAgICAgICAgICkgJT4lCiAgcmlnaHRfam9pbihEZWx0YURhdGF3TG9jYXRpb25zLCBieSA9IGMoIkRlbHRhcyIgPSAiRGVsdGEiKSkKCiNyZW5hbWUgdGhlIG1vbnRocyBieSBudW1iZXJzIGFuZCB0aWR5IHRoZSBHUkRDIGRhdGEKRGVsdGFzRGlzY2hhcmdlIDwtIERlbHRhc0dSREMgJT4lCiAgcmVuYW1lKERlbHRhID0gRGVsdGFzLCIxIiA9IEphbnVhcnksICIyIj0gRmVicnVhcnksICIzIj0gTWFyY2gsICI0Ij0gQXByaWwsCiAgICAgICAgICI1Ij1NYXksICI2Ij1KdW5lLCAiNyI9SnVseSwgIjgiPSBBdWd1c3QsICI5IiA9IFNlcHRlbWJlciwgIjEwIj1PY3RvYmVyLCAKICAgICAgICAgIjExIj1Ob3ZlbWJlciwgIjEyIj1EZWNlbWJlcikgJT4lCiAgc2VsZWN0KERlbHRhLCAiMSIgLCAiMiIgLCAiMyIsICI0IiwiNSIsICI2IiwgIjciLCAiOCIsICI5IiwgIjEwIiwgIjExIiwgIjEyIikgJT4lCiAgcGl2b3RfbG9uZ2VyKC1EZWx0YSwgbmFtZXNfdG8gPSAibW9udGgiLCB2YWx1ZXNfdG8gPSAiZGlzY2hhcmdlIikKCkRlbHRhc0Rpc2NoYXJnZSRtb250aCA9IGFzLm51bWVyaWMoRGVsdGFzRGlzY2hhcmdlJG1vbnRoKQoKRGlzTkRTU0kgPC0gRGVsdGFNZWFucyAlPiUgCiAgZmlsdGVyKHN1cmZhY2UgPT0gJ1dhdGVyJykgICU+JSAKICBzZWxlY3QgKC1jKE1lYW5ORFZJKSkgJT4lCiAgbGVmdF9qb2luKERlbHRhc0Rpc2NoYXJnZSwgYnkgPSBjKCdEZWx0YScsICdtb250aCcpKSAlPiUKICBkcm9wX25hKGRpc2NoYXJnZSkKCiNwbG90IG1vbnRobHkgbWVhbnMgYWdhaW5zdCBORFNTSSBmb3IgZGVsdGFzIHdpdGggR1JEQyBkaXNjaGFyZ2UgZGF0YQpnZ3Bsb3QoRGlzTkRTU0ksIGFlcyh5PWRpc2NoYXJnZSwgeD1NZWFuTkRTU0kpKSArIGdlb21fcG9pbnQoc2hhcGU9MSkgKyBmYWNldF93cmFwKCB+IERlbHRhLCBuY29sPTQsICwgc2NhbGVzPSJmcmVlX3kiKQoKYGBgCgo=